css: Fix computation of pixels occupied by blur radius
authorBenjamin Otte <otte@redhat.com>
Mon, 3 Feb 2014 14:56:59 +0000 (15:56 +0100)
committerBenjamin Otte <otte@redhat.com>
Mon, 3 Feb 2014 20:38:16 +0000 (21:38 +0100)
These computations were done randomly in lots of places and more often
than not, they were also wrong.
This function was copied (with docs) from Firefox:
  http://lxr.mozilla.org/mozilla-central/source/gfx/2d/Blur.cpp

https://bugzilla.gnome.org/show_bug.cgi?id=723159

gtk/gtkcairoblur.c
gtk/gtkcairoblurprivate.h
gtk/gtkcssshadowsvalue.c
gtk/gtkcssshadowvalue.c

index 3f944e24297d39e42a50ed7b0bedca0085821f34..df989898b2c99ffed4d52ffa094341d002b91782 100644 (file)
@@ -251,3 +251,29 @@ _gtk_cairo_blur_surface (cairo_surface_t* surface,
   /* Inform cairo we altered the surfaces contents. */
   cairo_surface_mark_dirty (surface);
 }
+
+/**
+ * _gtk_cairo_blur_compute_pixels:
+ * @radius: the radius to compute the pixels for
+ *
+ * Computes the number of pixels necessary to extend an image in one
+ * direction to hold the image with shadow.
+ *
+ * This is just the number of pixels added by the blur radius, shadow
+ * offset and spread are not included.
+ * 
+ * Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
+ * approximating a Gaussian using box blurs.  This yields quite a good
+ * approximation for a Gaussian.  Then we multiply this by 1.5 since our
+ * code wants the radius of the entire triple-box-blur kernel instead of
+ * the diameter of an individual box blur.  For more details, see:
+ * http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
+ */
+#define GAUSSIAN_SCALE_FACTOR ((3.0 * sqrt(2 * G_PI) / 4) * 1.5)
+
+int
+_gtk_cairo_blur_compute_pixels (double radius)
+{
+  return floor (radius * GAUSSIAN_SCALE_FACTOR + 0.5);
+}
index e048bac8cd5c707f0e3d47f7a87d38d6110c130c..d3e2f9ac462ad90693b7e8c4c94a3165c88777b2 100644 (file)
@@ -29,8 +29,9 @@
 
 G_BEGIN_DECLS
 
-void            _gtk_cairo_blur_surface (cairo_surface_t *surface,
-                                         double           radius);
+void            _gtk_cairo_blur_surface         (cairo_surface_t *surface,
+                                                 double           radius);
+int             _gtk_cairo_blur_compute_pixels  (double           radius);
 
 G_END_DECLS
 
index b688594cc1b23ba2b09cde9befcc700f37966aee..eb32471cec0345349f09f8875aa66c5e5c5b4ba1 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "gtkcssshadowsvalueprivate.h"
 
+#include "gtkcairoblurprivate.h"
 #include "gtkcssshadowvalueprivate.h"
 
 #include <string.h>
@@ -309,7 +310,7 @@ _gtk_css_shadows_value_get_extents (const GtkCssValue *shadows,
   guint i;
   GtkBorder b = { 0 };
   const GtkCssValue *shadow;
-  gdouble hoffset, voffset, spread, radius;
+  gdouble hoffset, voffset, spread, radius, clip_radius;
 
   g_return_if_fail (shadows->class == &GTK_CSS_VALUE_SHADOWS);
 
@@ -323,11 +324,12 @@ _gtk_css_shadows_value_get_extents (const GtkCssValue *shadows,
       _gtk_css_shadow_value_get_geometry (shadow,
                                           &hoffset, &voffset,
                                           &radius, &spread);
+      clip_radius = _gtk_cairo_blur_compute_pixels (radius);
 
-      b.top = MAX (0, radius + spread - voffset);
-      b.right = MAX (0, radius + spread + hoffset);
-      b.bottom = MAX (0, radius + spread + voffset);
-      b.left = MAX (0, radius + spread - hoffset);
+      b.top = MAX (0, clip_radius + spread - voffset);
+      b.right = MAX (0, clip_radius + spread + hoffset);
+      b.bottom = MAX (0, clip_radius + spread + voffset);
+      b.left = MAX (0, clip_radius + spread - hoffset);
 
       border->top = MAX (border->top, b.top);
       border->right = MAX (border->right, b.right);
index cdfd8526126552a891b794d016df614ef36c4242..6dcd2dd798186284ab3941a3d35def8f5f153cf9 100644 (file)
 
 #include <math.h>
 
-/* The blur of _gtk_cairo_blur_surface only approximately ends at radius,
-   so we add an extra pixel to make the clips less dramatic */
-#define CLIP_RADIUS_EXTRA 4
-
 struct _GtkCssValue {
   GTK_CSS_VALUE_BASE
   guint inset :1;
@@ -327,7 +323,7 @@ gtk_css_shadow_value_start_drawing (const GtkCssValue *shadow,
 
   gdk_cairo_get_clip_rectangle (cr, &clip_rect);
 
-  clip_radius = radius + CLIP_RADIUS_EXTRA;
+  clip_radius = _gtk_cairo_blur_compute_pixels (radius);
 
   /* Create a larger surface to center the blur. */
   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
@@ -534,7 +530,7 @@ _gtk_css_shadow_value_paint_box (const GtkCssValue   *shadow,
 
   spread = _gtk_css_number_value_get (shadow->spread, 0);
   radius = _gtk_css_number_value_get (shadow->radius, 0);
-  clip_radius = radius + CLIP_RADIUS_EXTRA;
+  clip_radius = _gtk_cairo_blur_compute_pixels (radius);
   x = _gtk_css_number_value_get (shadow->hoffset, 0);
   y = _gtk_css_number_value_get (shadow->voffset, 0);